package framework.impl;

import agent.impl.ResultsImpl;
import agent.impl.RuleBaseConfiguration;
import framework.api.*;
import framework.event.EventProcessingOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class KieContainerImpl implements KieContainer {

    private KieProject kProject;
    private final KieRepository kr;
    private final String containerId;
    private final Map<String, KieBase> kBases = new ConcurrentHashMap<String, KieBase>();
    private final Map<String, KieSession> kSessions = new ConcurrentHashMap<String, KieSession>();
    private static final Logger log = LoggerFactory.getLogger(KieContainerImpl.class);
    private ReleaseId containerReleaseId;

    public KieContainerImpl(String containerId, KieProject kProject, KieRepository kr) {
        this.kr = kr;
        this.kProject = kProject;
        this.containerId = containerId;
        kProject.init();
    }

    public KieSession newKieSession(String kSessionName) {
        KieSessionModelImpl kSessionModel = (KieSessionModelImpl) getKieSessionModel(kSessionName);

        KieBase kBase = getKieBase( kSessionModel.getKieBaseModel().getName() );
        if ( kBase == null ) {
            log.error("Unknown KieBase name: " + kSessionModel.getKieBaseModel().getName());
            return null;
        }

        KieSession kSession = kBase.newKieSession(getKieSessionConfiguration( kSessionModel ), null );

        kSessions.put(kSessionModel.getName(), kSession);
        return kSession;
    }

    private KieSessionConfiguration getKieSessionConfiguration( KieSessionModel kSessionModel ) {
        KieSessionConfiguration ksConf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
        ksConf.setOption( kSessionModel.getClockType() );
        ksConf.setOption( kSessionModel.getBeliefSystem() );
        return ksConf;
    }

    public KieSessionModel getKieSessionModel(String kSessionName) {
        return kProject.getKieSessionModel(kSessionName);
    }

    public KieBase getKieBase(String kBaseName) {
        KieBase kBase = kBases.get( kBaseName );
        if ( kBase == null ) {
            KieBaseModelImpl kBaseModel = getKieBaseModelImpl(kBaseName);
            synchronized (kBaseModel) {
                kBase = kBases.get( kBaseName );
                if ( kBase == null ) {
                    ResultsImpl msgs = new ResultsImpl();
                    kBase = createKieBase(kBaseModel, kProject, msgs, null);
                    kBases.put(kBaseName, kBase);
                }
            }
        }
        return kBase;
    }
    private KieBaseModelImpl getKieBaseModelImpl(String kBaseName) {
        KieBaseModelImpl kBaseModel = (KieBaseModelImpl) kProject.getKieBaseModel(kBaseName);
        if (kBaseModel == null) {
            throw new RuntimeException( "The requested KieBase \"" + kBaseName + "\" does not exist" );
        }
        return kBaseModel;
    }

    private KieBase createKieBase(KieBaseModelImpl kBaseModel, KieProject kieProject, ResultsImpl messages, KieBaseConfiguration conf) {
        InternalKieModule kModule = kieProject.getKieModuleForKBase( kBaseModel.getName() );
        Collection<KnowledgePackage> pkgs = kModule.getKnowledgePackagesForKieBase(kBaseModel.getName());

        if ( pkgs == null ) {
            KnowledgeBuilder kbuilder = AbstractKieModule.buildKnowledgePackages(kBaseModel, kieProject, messages);
            /*if ( kbuilder.hasErrors() ) {
                // Messages already populated by the buildKnowlegePackages
                return null;
            }*/
        }

        // if we get to here, then we know the pkgs is now cached
        pkgs = kModule.getKnowledgePackagesForKieBase(kBaseModel.getName());

        /*if ( kBaseModel.getEventProcessingMode() == EventProcessingOption.CLOUD &&
                (conf == null || conf.getOption(EventProcessingOption.class) == EventProcessingOption.CLOUD ) ) {
            for (KnowledgePackage kpkg : pkgs) {
                if ( ((KnowledgePackageImpl) kpkg).needsStreamMode() ) {
                    throw new RuntimeException( "The requested KieBase \"" + kBaseModel.getName() + "\" has been set to run in CLOUD mode but requires features only available in STREAM mode" );
                }
            }
        }*/

        ClassLoader cl = kieProject.getClassLoader();
        if (conf == null) {
            conf = getKnowledgeBaseConfiguration(kBaseModel, cl);
        } else if (conf instanceof RuleBaseConfiguration) {
            ((RuleBaseConfiguration)conf).setClassLoader(cl);
        }
        InternalKnowledgeBase kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase( kBaseModel.getName(), conf );
        kBase.setResolvedReleaseId(containerReleaseId);
        kBase.setContainerId(containerId);
        //kBase.initMBeans();

        kBase.addKnowledgePackages( pkgs );
        return kBase;
    }

    private KieBaseConfiguration getKnowledgeBaseConfiguration(KieBaseModelImpl kBaseModel, ClassLoader cl) {
        KieBaseConfiguration kbConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, cl);
        kbConf.setOption(kBaseModel.getEqualsBehavior());
        kbConf.setOption(kBaseModel.getEventProcessingMode());
        kbConf.setOption(kBaseModel.getDeclarativeAgenda());
        return kbConf;
    }

    public void dispose() {

    }
}
